import { ArchetypeMaskUtil } from "./Archetype";
import { Component } from "./Component";
import { GenericPool, IPoolable } from "./GenericPool";
import { World } from "./World";
import { GenericLinkedListNode } from "./GenericLinkedList";
import { EntityArchetypeChangedEvent } from "./Events";

export enum EntityStatus {
    Active,
    Inactive,
    Destroyed,
}

export class Entity implements IPoolable {
    private _id: number = 0;
    private readonly _maskComponents: Map<bigint, Component> = new Map();
    // private readonly _components: Component[] = [];     // component的添加和删除频率没那么高（如果很高的，可以考虑使用map）
    private _world: World;
    private _status: EntityStatus = EntityStatus.Active;

    // 保存当前的componentmask
    private _atMask: bigint = 0n;
    // private _archetype:Archetype;

    // 实体在链表中的节点引用
    private _entityListNode: GenericLinkedListNode<Entity> | null = null;
    // 实体在原型链表中的节点引用
    private _atlistNode: GenericLinkedListNode<Entity> | null = null;

    // 获取实体在链表中的节点
    public get entityListNode(): GenericLinkedListNode<Entity> | null {
        return this._entityListNode;
    }

    // 设置实体在链表中的节点
    public set entityListNode(node: GenericLinkedListNode<Entity> | null) {
        this._entityListNode = node;
    }

    // 获取实体在原型链表中的节点
    public get archetypeListNode(): GenericLinkedListNode<Entity> | null {
        return this._atlistNode;
    }

    // 设置实体在原型链表中的节点
    public set archetypeListNode(node: GenericLinkedListNode<Entity> | null) {
        this._atlistNode = node;
    }

    get componentMask() {
        return this._atMask;
    }

    public get id(): number {
        return this._id;
    }

    public get status(): EntityStatus {
        return this._status;
    }

    public set status(status: EntityStatus) {
        this._status = status;
    }

    public set id(id: number) {
        this._id = id;
    }

    constructor(world: World) {
        this._world = world;
    }

    public get world(): World {
        return this._world;
    }

    /**
     * 拷贝一份新的，不建议高频调用.
     */
    public getComponents(out: Component[]) {
        out.length = 0;
        this._maskComponents.forEach((component) => {
            out.push(component);
        });
        return out;
    }

    public addComponent<T extends Component>(constructor: new () => T): T {
        const typeMask = Component.getMask(constructor);
        if (this._maskComponents.has(typeMask)) {
            throw new Error(`Component ${Component.getTypeName(constructor)} already exists`);
        }

        const oldMask = this._atMask;

        let component = this._world.componentPool.get(typeMask);
        if (component) {
            component.onReuse();
        } else {
            component = new constructor();
        }

        this._maskComponents.set(component.typeMask, component);
        this._atMask = ArchetypeMaskUtil.add(this._atMask, component.typeMask);
        component.entity = this;

        if (this.status != EntityStatus.Active) {
            // 原型迁移
            this.world.entityManager.migrateArchetype(this, oldMask);

            // 处理组
            this.world.groupManager.onEntityChanged(this);

            // 事件
            this.world.triggerEvent(new EntityArchetypeChangedEvent(this, oldMask, this._atMask));
        }

        return component as T;
    }

    public removeComponent<T extends Component>(componentType: { prototype: T }): boolean {
        if (this.status != EntityStatus.Active) {
            throw new Error('can not remove component from destroyed entity.');
        }

        const atMask = Component.getMask(componentType);
        const component = this._maskComponents.get(atMask);
        if (!component) {
            return false;
        }

        const oldMask = this._atMask;

        // 处理组
        this.world.groupManager.onEntityChanged(this);

        this._maskComponents.delete(atMask);
        component.entity = null;
        ArchetypeMaskUtil.erase(this._atMask, atMask);

        // 原型迁移
        this.world.entityManager.migrateArchetype(this, oldMask);

        // 事件
        this.world.triggerEvent(new EntityArchetypeChangedEvent(this, oldMask, this._atMask));

        return true;
    }

    public getComponent<T extends Component>(componentType: { prototype: T }): T {
        const typeMask = Component.getMask(componentType);
        const component = this._maskComponents.get(typeMask) as T;
        if (!component) {
            throw new Error(`Component ${Component.getTypeName(componentType)} not found`);
        }
        return component;
    }

    public getOrAddComponent<T extends Component>(componentType: new () => T): T {
        const component = this.getComponent(componentType);
        if (!component) {
            return this.addComponent(componentType);
        }
        return component;
    }

    public getComponentOfMask(mask: bigint): Component | undefined {
        return this._maskComponents.get(mask);
    }

    public tryGetComponent<T extends Component>(componentType: new () => T): T | undefined {
        const typeMask = Component.getMask(componentType);
        return this._maskComponents.get(typeMask) as T;
    }

    public hasComponent<T extends Component>(componentType: new () => T): boolean {
        const typeMask = Component.getMask(componentType);
        return this._maskComponents.get(typeMask) as T != undefined;
    }


    public destroy() {
        this._world.removeEntity(this);
    }

    onRecycle() {
        // 回收所有的component
        this._maskComponents.forEach((component) => {
            component.onRecycle();
            component.entity = null;
            this.world.componentPool.put(component);
        });

        this._maskComponents.clear();
        this._atMask = 0n;
        this._atlistNode = null;
        this._entityListNode = null;
    }

    onReuse() {
        this._status = EntityStatus.Active;
    }
}

export class EntityPool extends GenericPool<number, Entity> {
    constructor() {
        super((entity: IPoolable) => (entity as Entity).id);
    }
}
